Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make TLS verification of upstream servers configurable #49

Merged
merged 2 commits into from
Oct 1, 2018

Conversation

sporkmonger
Copy link
Contributor

When pointing the proxy directly at an AWS ALB, e.g., the cert presented by the ALB is guaranteed to be invalid, as you can't get a cert for *.elb.amazonaws.com. Depending on your deployment setup you may or may not be able to easily automate creation of corresponding DNS records to match your certs. And of course your certs might be self-signed. Therefore it's desirable to be able to disable TLS verification for upstream servers in some cases. Particularly because the real security barrier is the necessary firewall rule that ensures nothing else can talk to the upstream server.

Fixes #47.

Copy link

@shrayolacrayon shrayolacrayon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for opening @sporkmonger, some initial requests!

@@ -48,6 +48,7 @@ type UpstreamConfig struct {

SkipAuthCompiledRegex []*regexp.Regexp
AllowedGroups []string
TLSVerify bool

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like in this case, the default for TLSVerify would be false, which would be a less secure default. Can you change this field to be InsecureSkipVerify, which when set to true, will enable InsecureSkipVerify? This will allow us to default to a more secure configuration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, and agree that default should be to verify. I meant to address that and forgot. However InsecureSkipVerify is a good name only inside a config struct named TLSConfig. It's not obvious that it's even TLS related outside that context. Given that, I'd be inclined to explicitly set a default rather than relying on zero values.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we make the name TLSSkipVerify?

Copy link
Contributor Author

@sporkmonger sporkmonger Sep 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main reason I'd choose TLSVerify over TLSSkipVerify is that TLSVerify / TLS_VERIFY / --tls-verify and similar are in wide usage in other tools, whereas TLSSkipVerify would be relatively uncommon. I also generally don't like to let language details (like what Go's zero values are) to leak out into user-facing interface design, and I think of config as part of the interface design.

"--tls-verify": 23,400 search results
"TLSVerify": 12,800 search results
"--tls-skip-verify": 2,210 search results
"TLSSkipVerify": 1,600 search results

Copy link
Contributor Author

@sporkmonger sporkmonger Sep 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That said, there's certainly other tools using TLSSkipVerify, like Vault and KSonnet, so it's not without precedent. Funny how it seems to be mostly prevalent in Go apps. 😛

@@ -75,6 +76,7 @@ type OptionsConfig struct {
HeaderOverrides map[string]string `yaml:"header_overrides"`
SkipAuthRegex []string `yaml:"skip_auth_regex"`
AllowedGroups []string `yaml:"allowed_groups"`
TLSVerify bool `yaml:"tls_verify"`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) Can we add a unit test for this config value to ensure that it is being set?

Copy link
Contributor Author

@sporkmonger sporkmonger Sep 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely. I was having trouble understanding the test suite when I first wrote the PR, but a couple PRs in, I think it should be no problem.

@sporkmonger sporkmonger force-pushed the tls-verify-config branch 2 times, most recently from a0b0d5b to 5c24ded Compare September 18, 2018 22:26
@sporkmonger
Copy link
Contributor Author

Added tests for TLS verification, wanted to make sure everyone's OK w/ keeping the TLSVerify configuration key name and just explicitly setting a default value before proceeding.

@sporkmonger
Copy link
Contributor Author

Ended up writing the code for it because it turned out to be easier than I thought it would be, but it's super easy to switch if you do prefer TLSSkipVerify.

proxy.TLSVerify = proxy.RouteConfig.Options.TLSVerify
if proxy.RouteConfig.Options.TLSVerify != nil {
proxy.TLSVerify = *proxy.RouteConfig.Options.TLSVerify
}
Copy link
Contributor Author

@sporkmonger sporkmonger Sep 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One item of note, we don't want to set the explicit default value here because of the if proxy.RouteConfig.Options == nil short-circuit at the top of the function. Doing so causes half the test suite to fail. I put it in loadServiceConfigs instead.

@@ -75,6 +76,7 @@ type OptionsConfig struct {
HeaderOverrides map[string]string `yaml:"header_overrides"`
SkipAuthRegex []string `yaml:"skip_auth_regex"`
AllowedGroups []string `yaml:"allowed_groups"`
TLSVerify *bool `yaml:"tls_verify"`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it still makes sense to use TLSSkipVerify as a bool, which removes the need for any default setting on load of the service configs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I'll change it.

@sporkmonger
Copy link
Contributor Author

OK, it's been switched to TLSSkipVerify.

@@ -201,6 +201,110 @@ func TestNewReverseProxyHostname(t *testing.T) {

}

func TestNewReverseProxyTLSSkipVerifyFalse(t *testing.T) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While some of our tests in this file are not, we are trying to standardize on writing table driven tests in our codebase, could you consolidate these two tests into one function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer table driven tests, was just mirroring what was already there. Happy to convert over.

@sporkmonger
Copy link
Contributor Author

Does it make it easier to review if I squash earlier in the PR or at the end?

@shrayolacrayon
Copy link

At the end would be easier, thanks for asking, this also looks good to me if you'd like to rebase + squash, we can get this merged!

@sporkmonger sporkmonger force-pushed the tls-verify-config branch 2 times, most recently from e2eed96 to 780b504 Compare September 25, 2018 21:31
@sporkmonger
Copy link
Contributor Author

All rebased and squashed!


// RoundTrip round trips the request and deletes security headers before returning the response.
func (t *upstreamTransport) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := http.DefaultTransport.RoundTrip(req)
transport := &http.Transport{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than creating a new transport every time, can we make this initialize one once and reuse it instead? This will hopefully reduce the overhead of TCP connections made.

@shrayolacrayon
Copy link

Added one more request!

@sporkmonger
Copy link
Contributor Author

No worries! Downside is it's a little more repetitious this way, but because of the shape of the structure and pointers, it's a little more challenging to factor that out in a way that wouldn't be buggy. Open to ideas.

@@ -167,7 +154,21 @@ func (t *upstreamTransport) RoundTrip(req *http.Request) (*http.Response, error)
// It adds an X-Forwarded-Host header that is the request's host.
func NewReverseProxy(to *url.URL, config *UpstreamConfig) *httputil.ReverseProxy {
proxy := httputil.NewSingleHostReverseProxy(to)
proxy.Transport = &upstreamTransport{InsecureSkipVerify: config.TLSSkipVerify}
proxy.Transport = &upstreamTransport{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we can probably create a function that initializes the upstreamTransport{}, something like NewUpstreamTransport(insecureSkipVerify bool) so that we're not repeating the config code twice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I like that solution better.

@sporkmonger
Copy link
Contributor Author

OK, I think this should be ready to go. Squash again?

@sporkmonger
Copy link
Contributor Author

Thanks for the help getting the PR cleaned up!

@shrayolacrayon shrayolacrayon merged commit 9776b52 into buzzfeed:master Oct 1, 2018
@while1eq1
Copy link
Contributor

Beautiful. Ive already been using this prior to the merge. Thanks @sporkmonger

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants